[#ftl]
[@define "agile.MultiItemEditorListener"]
	onItemSelected:function(items){
		//summary: call-back when items are selected
		//items: an array of item {value:key,innerHTML:value}
	}
	,onItemAdded:function(item){
		//summary: call-back when an item is added
		//item: the added item {value:key,innerHTML:value}
	}
	,onItemRemoved:function(items){
		//summary: call-back when items are removed
		//items: an array of item {value:key,innerHTML:value}
	}
	,onItemEdited:function(item){
		//summary: call-back when an item is being edited
		//item: the edited item {value:key,innerHTML:value}
	}
	,onValueChanged:function(value){
		//summary: call-back when the itemStore(MultiSelect) value is changed
		//value: the value of the serialized value of item store
	}
[/@]

[@define "agile.MultiItemEditor" "_WidgetBase,_TemplatedMixin,_WidgetsInTemplateMixin"]
//summary:advanced multi-select editor which can add/delete/edit the options through the option editor, the options will be shown at the itemStore(dijit/form/MultiSelect) widget
//example:
//	<div data-dojo-type="agile.MultiValueEditor" data-dojo-props='name:"abc",htmlName:"abc"'>	 	 
// 	 <div data-dojo-type='agile.NumberComboBox' class="editor" data-dojo-props='name:"xxx",htmlName:"xx",sequence:0,isKey:true'></div>
// 	 <div data-dojo-type='agile.NumberComboBox' class="editor" data-dojo-props='name:"yyy",htmlName:"yy",sequence:1'></div>
//	</div>
//	there are two editors defined at the example, it means 
//	1) each option label (displayed text) is composed by the value of these two editors 
//	2) when an option is selected in the itemStore(dijit/form/MultiSelect) widget, the option label (displayed text) will be decomposed 
//		and shown in the option editors  
//	3) 
//
 [#assign templateString]
 	<table cellspacing="3px" style="height:100%;width:100%;">
	<tr><td colspan="3"><div data-dojo-attach-point="containerNode">{this_editorTemplate}</div></td></tr>
	<tr>	
		<td><div data-dojo-type='dijit/form/TextBox' data-dojo-attach-point="itemKey" style="display:none"></div></td>
		<td width="10px"><img class='agileButton agileButtonAdd' data-dojo-attach-point="addButton" data-dojo-attach-event="onclick:addItem"></img></td>
		<td width="10px"><img class='agileButton agileButtonRemove' data-dojo-attach-point="removeButton" data-dojo-attach-event="onclick:removeItem"></img></td>
	</tr>
	<tr>
		<td colspan="3">
			<select data-dojo-type="dijit/form/MultiSelect" data-dojo-attach-point="itemStore" data-dojo-props='multiple:false,readOnly:true,style:"width:100%;height:50px"'></select>
		</td>
	</tr>	
	</table>
 [/#assign] 
 templateString:"${templateString?js_string}",itemDelimeter:",",value:null
 ,listener:null/*listener widget which the item editor belongs to*/
 ,keyParser:null/*the parser to parse the item key from item value*/
 ,editorTemplate:""	 
 ,constructor:function(){
	 aspect.after(this,"startup",lang.hitch(this,function(){
		 if(!this._initMultiItemEditor){
			 this.init();
			 this._initMultiItemEditor = true;
		 }
	 }));
 }
,postMixInProperties:function(){
	if(this.templateString){			
		this.templateString = lang.replace(this.templateString,agile.i18nValues(this.templateString,this));			
	}
}
  ,init:function(){
	//  console.log(this.declaredClass,"****[MultiItemEditor]init")
	  
	  	this.initItemStore(this.itemStore);
	  	
	  	//discover the editors and key editors	
		this.editors = []; this.keyEditors = [];
		query(".editor",this.containerNode).forEach(function(node,index){
			var editor = registry.byNode(node);
			this.editors.push(editor);
			if(editor.isKey){
				this.keyEditors.push(editor);
			}
//			agile.onEnterKeyPress(editor,lang.hitch(this,"addItem"));
			
			editor.watch("value",lang.hitch(this,function(name,old,value){				
				if(this.listener){
					this.listener.onItemEdited(this.compose());	
				}
			}));
		},this);
		
		this.own(on(this.itemStore,"click",lang.hitch(this,function(evt){
			var items = this.itemStore.getSelectedItems();
			if(items.length > 0){
				this.decompose(items[0]);
			}
			if(this.listener){
				this.listener.onItemSelected(items);
			}
		})));
	}
  	
  	,addItem:function(){  		
  		var item = this.compose();
  		if(item){
  			this.itemStore.replaceItems([item]);  			
  			//call value setter to notify value watch callbacks
  			//this.inherited("set",["value",this.get("value")]);
  			
  			if(this.listener){
				this.listener.onItemAdded(item);
				this.listener.onValueChanged(this.get("value"));
			}
  		}
  		//clear the editors
		this.decompose();
  	}
  	,removeItem:function(){
  		//clear all editors
  		for(var i = 0; i < this.editors.length; i++){
 			this.editors[i].reset1("value","");
 		}
  		//remove selected items from the item store
  		var items = this.itemStore.getSelectedItems();
  		if(items && items.length > 0){
  			this.itemStore.removeItems(items);
  			//call value setter to notify value watch callbacks
  			//this.inherited("set",["value",this.get("value")]);
  			if(this.listener){
				this.listener.onItemRemoved(items);
				this.listener.onValueChanged(this.get("value"));
			}
  		}
  	}
 	,get:function(name){
		if(name == "value"){
			//get all options from the selector and compose them to text
			var text = this.format(this.itemStore.getItems());
			//console.log("#######get value=",text,this.itemStore.getItems);
			return text;
		}
		return this.inherited(arguments);
	}
 	,set:function(name,value){
		if(name == "value" || name == "disabled" || name == "readOnly"){
			this.reset1(name,value);
		}else{
			this.inherited(arguments);
		}
	}
 	,reset1:function(name,value){
		if(name == "value"){
//			console.log(this.declaredClass,"reset1:",value);
			this.itemStore.reset1("value",this.parse(value));
			//show the first item in the editors
//			var items = this.itemStore.getItems();
//			this.decompose(items.length > 0?items[0]:"");
			//clear the editors
			this.decompose();
		}else if(name == "disabled" || name == "readOnly"){
			this.itemStore.set(name,value);			
			style.set(this.addButton,"visibility",value?"hidden":"visible");
			style.set(this.removeButton,"visibility",value?"hidden":"visible");
			array.forEach(this.editors,function(editor){
				editor.set(name,value);
			});
		}
	}
 	,getKey:function(){
 		//summary: the unique key for an option to support update feature, otherwise a new option will be created
 		var key = "";
 		if(this.keyEditors && this.keyEditors.length){
 			//get default key for an existing or new item 
	 		for(var i = 0; i < this.keyEditors.length; i++){
	 			key += this.keyEditors[i].get("value");
	 		}
 		}else{
 			//get default key for an existing item
 			key = this.itemKey.get("value");
 	 		if(!key){//generate key for a new item
 	 			key = "k" + (this.itemStore.getSize() + 1);
 	 		}
 		}
// 		console.log("item key:",key);
 		return key.trim(); 		
	}
 	,format:function(items){
 		//summary: convert the items to string
 		var text = ""
 		array.forEach(items,function(item){
 			text += item.innerHTML + this.itemDelimeter;
 		},this);
 		if(text != ""){//remove the last item delimeter
 			text = text.substring(0,text.length - 1);
 		}
 		return text;
 	}
 	,parse:function(text){
 		var items = []; 		
 		if(text){//convert text to items using default key
 			array.forEach(text.split(this.itemDelimeter),lang.hitch(this,function(value,index){
 				if(this.keyParser){
 					items.push({value:this.keyParser.parse(value),innerHTML:value});	
 				}else{
 					items.push({value:"k"+index,innerHTML:value});
 				}
 			}));
 		}
 		return items;
 	}
 	,compose:function(){
 		var item = null;
 		//summary:compose the option editors' value and return an object of {key,text} where key is the option value and text is the option label 		
 		 if(this.isValid()){
	 		var value = "";
	 		for(var i = 0; i < this.editors.length; i++){
	 			value += this.editors[i].get("value") + " ";
	 		}
	 		value = value.replace(/null/g,"").trim();
	 		if(value){
	 			item = {value:this.getKey(),innerHTML:value};
	 		}
 		 }
// 		 console.log("########compose item=",item,this.getKey())
 		 return item; 		
	}
 	,decompose:function(item){
 		//summary: decompose the text and set value for the editors
		 if(item && item.innerHTML){
	 		var values = item.innerHTML.split(" ");
	 		for(var i = 0; i < this.editors.length; i++){
	 			this.editors[i].reset1("value",values[i]?values[i]:"");
	 		}
	 		
	 		this.itemKey.set("value",item.value?item.value:"");
 		 }else{//set blank 
	 		for(var i = 0; i < this.editors.length; i++){
	 			this.editors[i].reset1("value","");
	 		}
	 		this.itemKey.set("value","");
 		 }
 	}
	,isValid:function(){	
		//summary: check whether all option editors have a valid value
		for(var i = 0; i < this.editors.length; i++){
			if(!this.editors[i].isValid()){
				return false;
			}
 		}
		
		return true;
 	}
	,initItemStore:function(multiSelect){
		
		agile.stopKeyEvent(multiSelect);
		
		lang.mixin(multiSelect,{
			replaceItems:function(items/*[{value:key,innerHTML:value}]*/){
				array.forEach(items,function(item){					
					//update innerHTML for existing option nodes in the select
					var isNew = true;
					var options = this.containerNode.options;
					for(var i = options.length - 1; i >= 0; i--){
						if(options[i].value == item.value){
							options[i].innerHTML = item.innerHTML;
							isNew = false;
							break;
						}
					}
					//create a new option node for new item
					if(isNew){
						construct.place(lang.replace("<option value='{value}'>{innerHTML}</option>",item), this.containerNode,'last');
					}
				},this);
			}
			,removeItems:function(items){
				if(items == undefined){//remove all existing items
					var options = this.containerNode.options;
					for(var i = options.length - 1; i >= 0; i--){
						construct.destroy(options[i], this.containerNode);
					}	
				}
				else{
					//remove the specified items
					array.forEach(items,function(item/*{value:key,innerHTML:value}*/){	
						//destroy option nodes 
						var options = this.containerNode.options;
						for(var i = options.length - 1; i >= 0; i--){
							if(options[i].value == item.value){
								construct.destroy(options[i], this.containerNode);
								break;
							}
						}					
					},this);
				}
			}
			,getItems:function(){
				//summary: get all the option nodes
				var items = [];
				query("option",this.containerNode).forEach(function(opt){
					items.push({value:opt.value,innerHTML:opt.innerHTML});
				},this);
//				console.log("getItems:",items);
				return items;
			}
			,getSize:function(){
				return query("option",this.containerNode).length;
			}
			,getSelectedItems:function(){
				//summary: get all selected option nodes
				var items = [];								
				array.forEach(this.getSelected(),function(opt){					
					items.push({value:opt.value,innerHTML:opt.innerHTML});					
				},this);
//				console.log("getSelectedItems:",items);
				return items;
			}
			,hasItem:function(item/*{value:key,innerHTML:value}*/){
				
				return array.filter(this.containerNode.options,function(option){					
					return option.value == item.value;
				})[0];
			}		
			,reset1:function(name,items){
				if(name == "value"){
					this.removeItems();
					this.replaceItems(items);
				}
			}
		});
	}
[/@]
[#--
[@define "agile.MultiItemEditor" "_WidgetBase,_TemplatedMixin,_WidgetsInTemplateMixin,agile.MultiItemEditorMixin"]
[/@]--]